okhttp 网络请求解析

okhttp 网络请求解析

前言

虽然我们常常使用 Retrofit 配合 okhttp 一起使用,但 okhttp 其实有自己完整的 okhttp 请求流程

这篇文章的目的就是深入解读 okhttp 从应用层到底层的请求流程,同时解析 okhttp 中重要的组成部分 Interceptor 的工作原理

okhttp 基本使用

构造一个 okhttpClient

1
2
3
4
5
6
public final OkHttpClient client = new OkHttpClient.Builder()
// 加了日志的拦截器
.addInterceptor(new HttpLoggingInterceptor())
// 加了网络缓存
.cache(new Cache(cacheDir, cacheSize))
.build();

通过 client 去执行网络请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 同步请求
Response response = client.newCall(request).execute();
// 或者异步请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {

}

@Override
public void onResponse(Call call, Response response) throws IOException {

}
});

流程解析

okhttp 网络请求的发生在 OkHttpClient.newCall 方法返回的 Call 对象执行的同步 execute 或者异步 enqueue 方法,而 Call 是一个接口,那我们追踪到 OkHttpClient.newCall 方法查找 Call 的实例对象

OkHttpClient.newCall 方法

1
2
3
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}

RealCall.newRealCall 静态方法

1
2
3
4
5
6
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
// 添加 call 的事件监听,来源于 client 的 eventListener 相关配置
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.eventListener = client.eventListenerFactory().create(call);
return call;
}

找到真正的 Call 实例,我们继续追踪 execute 方法

RealCall.execute 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override 
public Response execute() throws IOException {
// 通过 executed 进行同步控制
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
// 轨迹追踪打印
captureCallStackTrace();
// 监听事件触发
eventListener.callStart(this);
try {
// 这里触发网络请求
client.dispatcher().executed(this);
// 从拦截调用链上获取结果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
// 完成网络请求的回调
client.dispatcher().finished(this);
}
}

继续追踪到 Dispatcher.executed 方法

Dispatcher.executed 方法

1
2
3
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}

这里 RealCall 被加入到了一个叫 runningSyncCalls 的队列,从字面意义上看就是运行中的同步队列,我们的追踪到这里只能结束了,难道说由于这是个同步方法,所以触发流程并不在这里,这里只是记录了一下运行中的请求

这样的话只能返回去,从 getResponseWithInterceptorChain 方法追踪

RealCall.getResponseWithInterceptorChain 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Response getResponseWithInterceptorChain() throws IOException {
// 这里涉及到拦截器的使用
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());

return chain.proceed(originalRequest);
}

追踪到 Interceptor.Chain.proceed 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {

... 前置校验

// 这里就是拦截器执行的关键代码,通过 index + 1,会在下一个链子中执行对应的 interceptor 代码
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

... 后置校验

return response;
}

这里需要提到 okhttp 的设计模式——责任链模式,责任链的定义是使对象有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。在这里一大堆 Interceptor 形成了 InterceptorChain,而请求则会沿着链子传播处理,如果用图表示


在 Chain.proceed 中会生成下一个 nextChain,然后会调用对应的 Interceptor.intercept 方法并传入 nextChain,以伪代码展示 Interceptor.intercept 方法

Interceptor.intercept 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override public Response intercept(Chain chain) throws IOException {
// 获取请求
Request request = chain.request();

... 请求处理

// 获取返回
Response response = chain.process(request);

... 返回结果处理

return response;
}

这就完成了一个链式处理外层 intercept 提供处理后的request 给内层,内层返回 response 给外层,所以 intercept 的添加顺序会影响调用顺序,最先添加的则是最外层调用,而且我们可以得知最终的网络调用是在 CallServerInterceptor 里,使用了 Okio 操作 io 流。

现在我们回到 enqueue 异步操作方法去看看异步是如何执行的,RealCall 中,只是调用了 client.dispatcher().enqueue(new AsyncCall(responseCallback)),而真正生成调用链应该会在 Dispatch.enqueue 中,最终追踪到 Dispatch.enqueue 方法中

Dispatch.enqueue 方法

1
2
3
4
5
6
7
8
9
10
synchronized void enqueue(AsyncCall call) {
// 对同时进行的异步请求做数量限制
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
// 通过线程池执行 AsyncCall 的 run 方法
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}

分析 AsyncCall 的数据结构可以,真正的调用是在 AsyncCall.execute 方法中

AsyncCall.execute 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}

可以看到这里面的调用方法和同步方法里面基本一样,只是返回值交给了 callback 处理

结语

我们已经解析了请求整个请求过程,当然具体的各个 Interceptor 的逻辑没有深究,如果感兴趣可以去看看源码,至此,网络三剑客的解析已经全部完成